﻿using System;
using System.IO;
using System.Linq;
using System.Text;
using System.Data.SqlClient;
using System.Data;
using Log;

namespace RegLog
{
    /// <summary>
    /// Classe para registro de log em base de dados do Microsoft SQL Server
    /// </summary>
    public class LogMSSQL : ILog
    {

        private string Senha { get; set; }
        private string Usuario { get; set; }
        private string Servidor { get; set; }
        private string Porta { get; set; }
        private string DB { get; set; }
        private string Tabela { get; set; }
        private string ConnectionString { get; set; }
        private SqlConnection Conexao { get; set; }

        /// <summary>
        /// Construtor da classe de registro de logs em base de dados do MS SQL Server.
        /// </summary>
        /// <param name="servidor">Nome do servidor de banco de dados.</param>
        /// <param name="db">Nome do banco de dados.</param>
        /// <param name="usuario">Usuário com acesso ao banco de dados.</param>
        /// <param name="senha">Senha do usuário.</param>
        /// <param name="nomeTabela">Nome da tabela de registro de logs.</param>
        public LogMSSQL(string servidor, string db, string usuario, string senha, string nomeTabela)
        {
            Servidor = servidor;
            DB = db;
            Senha = senha;
            Usuario = usuario;
            Tabela = nomeTabela;
            ConnectionString = string.Format("Server={0};Database={1};User Id={2};Password={3};", Servidor, DB, Usuario, Senha);
            try
            {
                Conectar();
            }
            catch
            {
                if (VerificarBanco())
                    Conectar();
                else
                    throw new Exception("Banco de dados não encontrado ou o usuário não possui permissão de acesso.");
            }
            if (!VerificarTabela()) CriarTabelaLog();	     
        }

        /// <summary>
        /// Construtor da classe de registro de logs em base de dados do MS SQL Server.
        /// </summary>
        /// <param name="servidor">Endereço IP do servidor de banco de dados.</param>
        /// <param name="porta">Número da porta de conexão com o MS Sql Server.</param>
        /// <param name="catalog">Nome do banco de dados.</param>
        /// <param name="usuario">Usuário com acesso ao banco de dados.</param>
        /// <param name="senha">Senha do usuário.</param>
        /// <param name="nomeTabela">Nome da tabela de registro de logs.</param>
        public LogMSSQL(string servidor, int porta, string catalog, string usuario, string senha, string nomeTabela)
        {
            try
            {
                Servidor = servidor;
                DB = catalog;
                Usuario = usuario;
                Senha = senha;
                Porta = porta.ToString();
                Tabela = nomeTabela;
                ConnectionString = string.Format("Data Source={0},{1};Network Library=DBMSSOCN;Initial Catalog={2};User ID={3};Password={4};",
                    Servidor, Porta, DB, Usuario, Senha);
                try
                {
                    Conectar();
                }
                catch
                {
                    if (VerificarBanco())
                        Conectar();
                    else
                        throw new Exception("Banco de dados não encontrado ou o usuário não possui permissão de acesso.");
                }
                if (!VerificarTabela()) CriarTabelaLog();
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        private bool VerificarTabela()
        {
            var sql = string.Format("SELECT * FROM {0}.INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = '{1}'", DB, Tabela);
            if (Conexao == null) Conectar();
            var cmd = new SqlCommand(sql, Conexao);
            cmd.CommandType = System.Data.CommandType.Text;            
            var res = cmd.ExecuteReader();
            cmd.Dispose();
            bool ret = res.HasRows;
            res.Close();
            return ret;
        }

        private void Conectar()
        {
            try
            {
                if (Conexao == null) Conexao = new SqlConnection(ConnectionString);
                if (Conexao.State != ConnectionState.Open)
                {
                    Conexao.ConnectionString = ConnectionString;
                    Conexao.Open();
                }                
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        private bool VerificarBanco()
        {
            try
            {
                string  StringDeConexao = string.Format("Data Source={0},{1};Initial Catalog=master;User Id={2};Password={3};", Servidor, Porta, Usuario, Senha);
                if (Conexao == null) Conexao = new SqlConnection(StringDeConexao);
                Conexao.Open();
                var cmd = new SqlCommand(string.Format("SELECT * FROM sys.databases WHERE name = '{0}'", DB));
                var res = cmd.ExecuteReader();
                bool ret = res.HasRows;
                res.Close();
                cmd.Dispose();
                Conexao.Close();
                return ret;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        private void CriarTabelaLog()
        {
            try
            {
                Conectar();
                var sql = new StringBuilder();
                sql.AppendFormat("CREATE TABLE {0} (", Tabela);
                sql.AppendLine("dataHora DATETIME DEFAULT GETDATE() NOT NULL, ");
                sql.AppendLine("tipoLog NVARCHAR(20)  NOT NULL, ");
                sql.AppendLine("descricao NVARCHAR(200)  NOT NULL, ");
                sql.AppendLine("excecaoMensagem NVARCHAR(500)  NULL, ");
                sql.AppendLine("excecaoSource NVARCHAR(500)  NULL, ");
                sql.AppendLine("excecaoStackTrace NVARCHAR(500)  NULL, ");
                sql.AppendLine("excecaoTargetSite NVARCHAR(500)  NULL)");
                var cmd = new SqlCommand(sql.ToString(), Conexao);
                cmd.ExecuteNonQuery();
                cmd.Dispose();
            }
            catch (Exception ex)
            {                
                throw ex;
            }
        }

        /// <summary>
        /// Registra o evento de log no banco de dados do MS SQL Server.
        /// </summary>
        /// <param name="sender">Objeto responsável pelo registro do log.</param>
        /// <param name="e">Objeto que representa o evento de log.</param>
        /// <param name="ret">Retorno que informa sucesso ou falha (-1) no registro de log.</param>
        public void Log(object sender, EventosLogEventArgs e, out int ret)
        {
            Conectar();
            using (SqlTransaction tr = Conexao.BeginTransaction())
            {
                try
                {
                    var cmd = Conexao.CreateCommand();
                    cmd.Transaction = tr;

                    var sql = new StringBuilder();
                    sql.AppendFormat("INSERT INTO {0} (dataHora, tipoLog, descricao, ", Tabela);
                    sql.AppendLine("excecaoMensagem, excecaoSource, excecaoStackTrace, excecaoTargetSite) ");
                    sql.AppendFormat("VALUES ('{0}','{1}', '{2}', '{3}', '{4}', '{5}', '{6}')", e.DataHora.ToString(), e.TipoLogString, e.Descricao, e.Exception.Message,
                        e.Exception.Source, e.Exception.StackTrace, e.Exception.TargetSite); //"yyyy-MM-dd HH:mm:ss"
                    cmd.CommandText = sql.ToString();
                    ret = cmd.ExecuteNonQuery();
                    tr.Commit();
                    cmd.Dispose();
                }
                catch (Exception ex)
                {
                    ret = -1;
                    tr.Rollback();
                    throw ex;
                }
            }
            Conexao.Close();            
        }
    }
}
